package cn.iocoder.yudao.module.pplan.service.recipe;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper;
import cn.iocoder.yudao.module.pplan.controller.admin.recipe.vo.RecipePageReqVO;
import cn.iocoder.yudao.module.pplan.controller.admin.recipe.vo.RecipeRespVO;
import cn.iocoder.yudao.module.pplan.controller.admin.recipe.vo.RecipeSaveReqVO;
import cn.iocoder.yudao.module.pplan.controller.admin.recipedetail.vo.RecipeDetailRespVO;
import cn.iocoder.yudao.module.pplan.controller.app.recipe.vo.AppRecipeDetailRespVO;
import cn.iocoder.yudao.module.pplan.controller.app.recipe.vo.AppRecipeMealIngeRespVO;
import cn.iocoder.yudao.module.pplan.controller.app.recipe.vo.AppRecipePageReqVO;
import cn.iocoder.yudao.module.pplan.controller.app.recipe.vo.AppRecipeRespVO;
import cn.iocoder.yudao.module.pplan.dal.dataobject.recipe.RecipeDO;
import cn.iocoder.yudao.module.pplan.dal.dataobject.recipedetail.RecipeDetailDO;
import cn.iocoder.yudao.module.pplan.dal.dataobject.recipemeal.RecipeMealDO;
import cn.iocoder.yudao.module.pplan.dal.dataobject.recipemealingre.RecipeMealIngreDO;
import cn.iocoder.yudao.module.pplan.dal.dataobject.tableware.TablewareDO;
import cn.iocoder.yudao.module.pplan.dal.mysql.recipe.RecipeMapper;
import cn.iocoder.yudao.module.pplan.dal.mysql.recipedetail.RecipeDetailMapper;
import cn.iocoder.yudao.module.pplan.dal.mysql.recipemeal.RecipeMealMapper;
import cn.iocoder.yudao.module.pplan.dal.mysql.recipemealingre.RecipeMealIngreMapper;
import cn.iocoder.yudao.module.pplan.dal.mysql.tableware.TablewareMapper;
import cn.iocoder.yudao.module.pplan.enums.PPlanAirlineClassEnum;
import cn.iocoder.yudao.module.pplan.enums.PPlanAirlineIdEnum;
import cn.iocoder.yudao.module.pplan.enums.PPlanAirlineTypeEnum;
import cn.iocoder.yudao.module.pplan.enums.PPlanMealServiceEnum;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
import static cn.iocoder.yudao.module.pplan.enums.ErrorCodeConstants.RECIPE_NOT_EXISTS;

/**
 * 生产计划-餐谱数据管理 Service 实现类
 *
 * @author yisl
 */
@Service
@Validated
public class RecipeServiceImpl implements RecipeService {

    @Resource
    private RecipeMapper recipeMapper;

    @Resource
    private RecipeDetailMapper recipeDetailMapper;

    @Resource
    private RecipeMealMapper recipeMealMapper;

    @Resource
    private TablewareMapper tablewareMapper;

    @Resource
    private RecipeMealIngreMapper recipeMealIngreMapper;

    @Resource
    private ErpProductMapper productMapper;


    @Override
    public Long createRecipe(RecipeSaveReqVO createReqVO) {
        // 插入
        RecipeDO recipe = BeanUtils.toBean(createReqVO, RecipeDO.class);
        recipe.setStartDate(createReqVO.getExpireDate()[0]);
        recipe.setEndDate(createReqVO.getExpireDate()[1]);
        recipe.setRecipeCode("MEAL-"+System.currentTimeMillis());
        // 状态为启用时，需要将其他航空公司、航线类型、舱位等级、餐食类别相同的状态改为停用
        if (recipe.getIsActive()){
            recipeMapper.updateRecipeisActiveByFE(recipe, false);
        }
        recipeMapper.insert(recipe);

        // 插入配料关联信息
        if (ObjectUtil.isNotEmpty(createReqVO.getRecipeDetailRespVOs())){
            List<RecipeDetailDO> recipeDetailDOS = BeanUtils.toBean(createReqVO.getRecipeDetailRespVOs(), RecipeDetailDO.class);
            recipeDetailDOS.forEach(item -> item.setRecipeId(recipe.getId()));
            recipeDetailMapper.insertBatch(recipeDetailDOS);
        }

        // 返回
        return recipe.getId();
    }

    @Override
    public void updateRecipe(RecipeSaveReqVO updateReqVO) {
        // 校验存在
        validateRecipeExists(updateReqVO.getId());
        // 更新
        RecipeDO updateObj = BeanUtils.toBean(updateReqVO, RecipeDO.class);
        updateObj.setStartDate(updateReqVO.getExpireDate()[0]);
        updateObj.setEndDate(updateReqVO.getExpireDate()[1]);
        // 状态为启用时，需要将其他航空公司、航线类型、舱位等级、餐食类别相同的状态改为停用
        if (updateObj.getIsActive()){
            recipeMapper.updateRecipeisActiveByFE(updateObj, false);
        }
        recipeMapper.updateById(updateObj);

        // 更新配料信息
        recipeDetailMapper.delete(RecipeDetailDO::getRecipeId, updateReqVO.getId());
        if (ObjectUtil.isNotEmpty(updateReqVO.getRecipeDetailRespVOs())) {
            List<RecipeDetailDO> recipeDetailDOS = BeanUtils.toBean(updateReqVO.getRecipeDetailRespVOs(), RecipeDetailDO.class);
            recipeDetailMapper.insertBatch(recipeDetailDOS);
        }

    }

    @Override
    public void deleteRecipe(Long id) {
        // 校验存在
        validateRecipeExists(id);
        // 删除
        recipeMapper.deleteById(id);
        recipeDetailMapper.delete(RecipeDetailDO::getRecipeId, id);
    }

    @Override
    public void deleteRecipeListByIds(List<Long> ids) {
        // 校验存在
        validateRecipeExists(ids);
        // 删除
        recipeMapper.deleteByIds(ids);
        recipeDetailMapper.deleteBatch(RecipeDetailDO::getRecipeId, ids);
    }

    private void validateRecipeExists(List<Long> ids) {
        List<RecipeDO> list = recipeMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(RECIPE_NOT_EXISTS);
        }
    }

    private void validateRecipeExists(Long id) {
        if (recipeMapper.selectById(id) == null) {
            throw exception(RECIPE_NOT_EXISTS);
        }
    }

    @Override
    public RecipeDO getRecipe(Long id) {
        return recipeMapper.selectById(id);
    }

    @Override
    public RecipeRespVO getRecipeRespVO(Long id) {
        RecipeDO recipeDO = recipeMapper.selectById(id);
        if (recipeDO == null) {
            throw exception(RECIPE_NOT_EXISTS);
        }
        RecipeRespVO respVO = BeanUtils.toBean(recipeDO, RecipeRespVO.class);
        if (ObjectUtil.isNotNull(respVO)) {
            List<RecipeDetailDO> recipeDetailDOS = recipeDetailMapper.selectList(RecipeDetailDO::getRecipeId, id);
            if (ObjectUtil.isNotEmpty(recipeDetailDOS)) {
                respVO.setRecipeDetailRespVOs(BeanUtils.toBean(recipeDetailDOS, RecipeDetailRespVO.class));
            }
        }

        return respVO;
    }

    @Override
    public PageResult<RecipeDO> getRecipePage(RecipePageReqVO pageReqVO) {
        return recipeMapper.selectPage(pageReqVO);
    }

    @Override
    public AppRecipeRespVO appGetRecipe(Long id) {
        RecipeDO recipeDO = recipeMapper.selectById(id);
        if (recipeDO == null) {
            throw exception(RECIPE_NOT_EXISTS);
        }
        AppRecipeRespVO respVO = BeanUtils.toBean(recipeDO, AppRecipeRespVO.class);

        //1.获取餐谱与菜品关系
        List<RecipeDetailDO> recipeDetailDOS = recipeDetailMapper.selectList(RecipeDetailDO::getRecipeId, id);
        //2.获取菜品列表
        List<Long> recipeMealIds = convertListByFlatMap(recipeDetailDOS,
                detailDO -> Stream.of(detailDO.getReceipeMealId(), detailDO.getId()));
        List<RecipeMealDO> recipeMealDOList = recipeMealMapper.selectByIds(recipeMealIds);
        Map<Long, RecipeMealDO> longRecipeMealDOMap = CollectionUtils.convertMap(recipeMealDOList, RecipeMealDO::getId);
        //3.获取餐具列表
        List<TablewareDO> tablewareDOList = tablewareMapper.selectByIds(convertListByFlatMap(recipeDetailDOS,
                detailDO -> Stream.of(detailDO.getTablewareId(), detailDO.getId())));
        Map<Long, TablewareDO> longTablewareDOMap = CollectionUtils.convertMap(tablewareDOList, TablewareDO::getId);

        //4.获取配料关联表
        List<RecipeMealIngreDO> mealIngreDOList = recipeMealIngreMapper.selectList(RecipeMealIngreDO::getReceipeMealId,recipeMealIds);
        //5.获取配料信息
        List<ErpProductDO> productDOS = new ArrayList<>();
        if (CollUtil.isNotEmpty(mealIngreDOList)) {
            productDOS = productMapper.selectByIds(convertListByFlatMap(mealIngreDOList,
                    detailDO -> Stream.of(detailDO.getProductId(), detailDO.getId())));
        }
        Map<Long, ErpProductDO> longErpProductDOMap = CollectionUtils.convertMap(productDOS, ErpProductDO::getId);
        //组装后的配料信息
        List<AppRecipeMealIngeRespVO> appRecipeMealIngeRespVOS = BeanUtils.toBean(mealIngreDOList, AppRecipeMealIngeRespVO.class, clueVO -> {
            MapUtils.findAndThen(longErpProductDOMap, clueVO.getProductId(), erpProductDO -> {
                clueVO.setProductName(erpProductDO.getName());
                clueVO.setStandard(erpProductDO.getStandard());
            });
        });
        Map<Long, List<AppRecipeMealIngeRespVO>> longListMap = appRecipeMealIngeRespVOS.stream().collect( Collectors.groupingBy(AppRecipeMealIngeRespVO::getReceipeMealId) );

        //组装后的菜品信息
        List<AppRecipeDetailRespVO> appRecipeDetailRespVOS = BeanUtils.toBean(recipeDetailDOS, AppRecipeDetailRespVO.class, clueVO -> {
            // 组装菜品名字
            MapUtils.findAndThen(longRecipeMealDOMap, clueVO.getReceipeMealId(), recipeMealDO ->
                clueVO.setRecipeMealName(recipeMealDO.getMealName()));
            // 组装餐具名字
            MapUtils.findAndThen(longTablewareDOMap, clueVO.getTablewareId(), tablewareDO ->
                    clueVO.setTablewareName(tablewareDO.getName()));
            // 组装配料信息
            MapUtils.findAndThen(longListMap, clueVO.getReceipeMealId(), clueVO::setAppRecipeMealIngeRespVOS);
        });


        //组装数据
        respVO.setCabinClassName(PPlanAirlineClassEnum.getDesc(respVO.getCabinClass()));
        respVO.setAirlineTypeName(PPlanAirlineTypeEnum.getDesc(respVO.getAirlineType()));
        respVO.setAirlineName(PPlanAirlineIdEnum.getDesc(respVO.getAirlineId()+""));
        respVO.setMealServiceName(PPlanMealServiceEnum.getDesc(respVO.getMealService()));
        respVO.setAppRecipeDetailRespVOS(appRecipeDetailRespVOS);

        return respVO;
    }

    @Override
    public PageResult<AppRecipeRespVO> appGetRecipePage(AppRecipePageReqVO pageReqVO) {
        PageResult<RecipeDO> recipeDOPageResult = recipeMapper.appSelectPage(pageReqVO);
        return new PageResult<>(buildRecipeDetailList(recipeDOPageResult.getList()), recipeDOPageResult.getTotal());

    }
    private List<AppRecipeRespVO> buildRecipeDetailList(List<RecipeDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }

        // 2. 转换成 VO
        return BeanUtils.toBean(list, AppRecipeRespVO.class, clueVO -> {
            clueVO.setCabinClassName(PPlanAirlineClassEnum.getDesc(clueVO.getCabinClass()));
            clueVO.setAirlineTypeName(PPlanAirlineTypeEnum.getDesc(clueVO.getAirlineType()));
            clueVO.setAirlineName(PPlanAirlineIdEnum.getDesc(clueVO.getAirlineId()+""));
            clueVO.setMealServiceName(PPlanMealServiceEnum.getDesc(clueVO.getMealService()));
        });
    }

}